using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using UniversalAdminSystem.Application.Common.Interfaces;
using UniversalAdminSystem.Infrastructure.Persistence.DbContexts;
using UniversalAdminSystem.Infrastructure.RabbitMQ.Models.Messages;

namespace UniversalAdminSystem.Infrastructure.RabbitMQ.Publishers;

public class FileProcessingJobPublisher : BackgroundService
{
  private readonly IServiceScopeFactory _sf;
  private readonly IEventBus _bus;
  private readonly ILogger<FileProcessingJobPublisher> _logger;

  public FileProcessingJobPublisher(IServiceScopeFactory sf, IEventBus bus, ILogger<FileProcessingJobPublisher> logger)
  { _sf = sf; _bus = bus; _logger = logger; }

  protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  {
    while (!stoppingToken.IsCancellationRequested)
    {
      await Task.Run(async () =>
      {
        try
        {
          using var scope = _sf.CreateScope();
          var db = scope.ServiceProvider.GetRequiredService<UniversalAdminSystemDbContext>();
          var jobs = await db.FileProcessingJobs
            .Where(x => x.Status == "Pending" && (x.NextAttemptAt == null || x.NextAttemptAt <= DateTime.UtcNow))
            .OrderBy(x => x.CreatedAt)
            .Take(100)
            .ToListAsync(stoppingToken);

          foreach (var j in jobs) j.Status = "Processing";
          await db.SaveChangesAsync(stoppingToken);

          foreach (var j in jobs)
          {
            var message = new FileProcessingMessage
            {
              Id = j.Id,
              FileId = j.FileId,
              FilePath = j.FilePath,
              ContentType = j.ContentType,
              Size = j.Size,
              RetryCount = j.RetryCount
            };
            var payload = JsonSerializer.Serialize(message);
            try
            {
              await _bus.PublishAsync("file-processing", "file-processing", payload, messageId: j.Id.ToString(), stoppingToken);
              j.Status = "Succeeded";
            }
            catch (Exception ex)
            {
              j.RetryCount++;
              j.Status = "Pending";
              j.NextAttemptAt = DateTime.UtcNow.AddSeconds(Math.Min(300, Math.Pow(2, j.RetryCount)));
              _logger.LogError(ex, "发布失败: {JobId}", j.Id);
            }
          }
          await db.SaveChangesAsync(stoppingToken);
        }
        catch (Exception ex) { _logger.LogError(ex, "文件处理任务发布循环错误"); }
      });
      
      await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
    }
  }
}